package com.songcan.gateway.handle;


import com.songcan.common.context.UserContext;
import com.songcan.common.vo.UserDetailsNew;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Component
@RefreshScope
public class AccessManager  implements ReactiveAuthorizationManager<AuthorizationContext> {


    @Value("${ignore.urls}")
    public Set<String> permitAll;
    @Value("${ignore.rest}")
    public Set<String> permitRest;
    @Value("${ignore.app}")
    public List<String> permitApp;

    private static final AntPathMatcher antPathMatcher = new AntPathMatcher();
    @Autowired
    private RedisTemplate redisTemplate;


    /**
     * 实现权限验证判断
     */
    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> authenticationMono, AuthorizationContext authorizationContext) {
        ServerWebExchange exchange = authorizationContext.getExchange();
        //请求资源
        String requestPath = exchange.getRequest().getURI().getPath();
        //请求方式
        String method = exchange.getRequest().getMethodValue();
        // 是否直接放行
        if (permitAll(requestPath)) {
            return Mono.just(new AuthorizationDecision(true));
        }
        return authenticationMono.map(auth ->{
            log.info("进行鉴权操作1");
           return new AuthorizationDecision(checkAuthorities(auth, requestPath,method));
        }).defaultIfEmpty(new AuthorizationDecision(false));



    }

    /**
     * 校验是否属于静态资源
     * @param requestPath 请求路径
     * @return
     */
    private boolean permitAll(String requestPath) {
        return permitAll.stream()
                .anyMatch(r -> antPathMatcher.match(r, requestPath));
    }

    //权限校验
    private boolean checkAuthorities(Authentication auth, String requestPath,String method) {
        Set<String> scope = new HashSet<>();
        if(auth instanceof OAuth2Authentication){
            OAuth2Authentication athentication = (OAuth2Authentication) auth;
            String clientId = athentication.getOAuth2Request().getClientId();
            log.info("clientId is {}",clientId);

            scope = ((OAuth2Authentication) auth).getOAuth2Request().getScope();
        }
        Object principal = auth.getPrincipal();
        log.info("用户信息:{}",principal.toString());
        UserDetailsNew userDetailsNew = UserDetailsNew.class.cast(principal);
        UserContext.set(String.valueOf(userDetailsNew.getUserId()),userDetailsNew.getOrganizations());
        if(userDetailsNew.getAuthorities().size()>0){
            UserContext.setRoles(userDetailsNew.getAuthorities().parallelStream().map(p->p.getAuthority()).collect(Collectors.toList()));
        }


        //进行 资源放通
        if(permitRest.parallelStream().anyMatch(p->antPathMatcher.match(p,requestPath))){
            return  true;
        }

        //前端教研
        if(permitApp.parallelStream().anyMatch(p->antPathMatcher.match(p,requestPath))){
            if(scope.contains("app")){
                return  true;
            }
        }
        //进行当前用户角色 资源鉴权
        boolean isPermission = false;
        if(principal instanceof UserDetailsNew){

            Collection<GrantedAuthority>  grantedAuthorities = userDetailsNew.getAuthorities();
            for (GrantedAuthority grantedAuthority : grantedAuthorities) {
                 String role = grantedAuthority.getAuthority();
                 String key = "AUTH:MENU:" +role+":"+method;
                 String s = (String) redisTemplate.opsForHash().get(key,requestPath);
                 if(Objects.nonNull(s)&&scope.contains(s)){
                     isPermission = true;
                     break;
                 }
            }
        }
        return isPermission;
    }
}
